package cn.funeralobjects.youtube.dl.extension.repository.impl;

import cn.funeralobjects.youtube.dl.extension.model.DownloadTask;
import cn.funeralobjects.youtube.dl.extension.repository.TaskRepository;
import cn.funeralobjects.youtube.dl.extension.utils.CSVUtils;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.util.Deque;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.stream.Collectors;

/**
 * @author FuneralObjects
 * Create date: 2020/4/8 1:59 AM
 */
public abstract class BaseTaskRepositoryImpl implements TaskRepository {

    private File dataFile;
    private Deque<DownloadTask> cache;

    @PostConstruct
    protected void init() throws IOException {
        this.dataFile = this.getDataFile();
        this.checkDataFile();
        this.cache = new ConcurrentLinkedDeque<>(readDataFromFile(this.dataFile));
    }

    @Override
    public List<DownloadTask> getAllTasks() {
        return cache.parallelStream().collect(Collectors.toList());
    }

    @Override
    public synchronized void push(DownloadTask task) {
        if(DownloadTask.isInvalidData(task)){
            throw new IllegalArgumentException("The arg of task is invalid!");
        }
        if(cache.parallelStream().anyMatch(task::equals)){
            return;
        }
        cache.addLast(task);
        syncCacheToDataFile();
    }

    @Override
    public synchronized void remove(String id, String format) {
        DownloadTask removed = null;
        for (DownloadTask task : cache) {
            if(task.getId().equals(id) && task.getFormat().equals(format)){
                removed = task;
                break;
            }
        }
        if(removed == null){
            return;
        }
        cache.removeFirstOccurrence(removed);
        syncCacheToDataFile();

    }

    @Override
    public synchronized DownloadTask pop() {
        DownloadTask task = cache.pop();
        if (task == null) {
            return null;
        }
        syncCacheToDataFile();
        return task;
    }

    @Override
    public DownloadTask getByIdAndFormat(String id, String format) {
        return getAllTasks().parallelStream().filter(task -> task.getId().equals(id) && task.getFormat().equals(format)).findFirst().orElse(null);
    }

    protected synchronized void syncCacheToDataFile() {
        CSVUtils.rewriteToFileForArray(cache.parallelStream().map(DownloadTask::convertRowData).collect(Collectors.toList()), this.dataFile);
    }

    /**
     * 获取文件路径
     *
     * @return 文件路径
     */
    protected abstract String getFilePath();

    protected File getDataFile(){
        return new File(getFilePath());
    }

    protected void checkDataFile() throws IOException {
        if( !this.dataFile.exists() || !this.dataFile.isFile()){
            File parent = this.dataFile.getParentFile();
            if(!parent.exists() || !parent.isDirectory()){
                if(!parent.mkdirs()){
                    throw new RuntimeException("The folder["+parent.getAbsolutePath()+"] creation failed!");
                }
            }
            if(!this.dataFile.createNewFile()){
                throw new RuntimeException("The file["+this.dataFile.getAbsolutePath()+"] creation failed!");
            }
        }
    }

    protected static List<DownloadTask> readDataFromFile(File file){
        if(file == null){
            throw new IllegalArgumentException("The arg of file cannot be null!");
        }
        return CSVUtils.readFile(file).parallelStream().map(DownloadTask::convertTask).collect(Collectors.toList());
    }
}
